package com.cuixichang.minimalism.controller;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cuixichang.minimalism.acl.RelevanceDesign;
import com.cuixichang.minimalism.basal.base.Result;
import com.cuixichang.minimalism.basal.cnstants.HttpHeader;
import com.cuixichang.minimalism.basal.enums.IsEnableEnum;
import com.cuixichang.minimalism.basal.enums.ResponseCoderEnum;
import com.cuixichang.minimalism.basal.exceptions.NoDataException;
import com.cuixichang.minimalism.core.security.design.JWTAnalysisDesign;
import com.cuixichang.minimalism.core.security.userdetails.CredentialDetail;
import com.cuixichang.minimalism.core.security.utils.SecurityBeanUtil;
import com.cuixichang.minimalism.domain.ICacheCommandService;
import com.cuixichang.minimalism.domain.ILoginCommandService;
import com.cuixichang.minimalism.transfer.dto.ILoginDTO;
import com.cuixichang.minimalism.transfer.factory.ILoginAssembler;
import com.cuixichang.minimalism.transfer.page.ILoginPageRequest;
import com.cuixichang.minimalism.transfer.page.ILoginPasswordRequest;
import com.cuixichang.minimalism.transfer.page.ILoginRequest;
import com.cuixichang.minimalism.transfer.page.ILoginUpdatePasswordRequest;
import com.cuixichang.minimalism.transfer.vo.ILoginVO;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
import com.cuixichang.minimalism.service.ILoginService;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import lombok.RequiredArgsConstructor;
import springfox.documentation.annotations.ApiIgnore;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 系统登录账户管理表表控制层
 *
 * @author cuixichang
 * @date 2024-11-22
 */
@Api(tags = "系统登录账户管理表对象功能接口")
@RestController
@RequestMapping("/login")
@RequiredArgsConstructor
public class ILoginController {
    @Autowired
    private ILoginService iLoginService;
    @Autowired
    private JWTAnalysisDesign jwtAnalysisDesign;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private ILoginCommandService iLoginCommandService;
    @Autowired
    private RelevanceDesign relevanceDesign;
    @Autowired
    private ICacheCommandService cacheCommandService;
    @GetMapping("/info")
    @ApiOperation(value = "查询当前登录账号基本信息")
    public Result<ILoginVO> current(@RequestParam(name = "账号拓展信息",defaultValue = "user") String moduleName) {
        CredentialDetail createLoginId = SecurityBeanUtil.getCredentialDetail();
        Assert.notNull(createLoginId, "当前用户未登录");
        ILoginDTO iLogin = iLoginCommandService.searchById(createLoginId.getLoginId(),moduleName,createLoginId);
        Optional.ofNullable(iLogin).orElseThrow(()->new NoDataException("查询当前登录账号异常", null));
        return Result.ok(ResponseCoderEnum.Success.getDesc(), ILoginAssembler.INSTANCT.convertVo(iLogin));
    }
    @PostMapping("/password")
    @ApiOperation(value = "用户登录准入口")
    public Result<String> login(@RequestBody ILoginRequest request,
                                @RequestHeader(HttpHeader.SYSTEM_SOURCE_CODE) @ApiIgnore String systemCode) {
        String loginName = request.getLoginName();
        if(Objects.isNull(loginName)){
            return Result.error(ResponseCoderEnum.NoLoginName.getCode(), ResponseCoderEnum.NoLoginName.getDesc());
        }
        Result<Boolean> result = hasCaptcha(loginName, systemCode);
        if(ResponseCoderEnum.Success.getCode().equals(result.getCode()) && result.getData()){
            String code = request.getCode();
            if(StrUtil.isBlank(code)){
                cacheCommandService.putCache(cacheCommandService.getKey(systemCode, loginName,"check"),
                        String.valueOf(IsEnableEnum.YES.getCode()));
                return Result.error(ResponseCoderEnum.NoCode.getCode(), ResponseCoderEnum.NoCode.getDesc());
            }else {
                String captcha = cacheCommandService.getValue(cacheCommandService.getKey(systemCode, loginName, "captcha"));
                if(!captcha.equals(code)){
                 cacheCommandService.putCache(cacheCommandService.getKey(systemCode, loginName,"check"),
                         String.valueOf(IsEnableEnum.YES.getCode()));
                return Result.error(ResponseCoderEnum.NoCode.getCode(), "验证码校验不匹配");
                }
            }
        }
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(request.getLoginName(), request.getLoginPassword());
        Authentication authenticate ;
        try {
             authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        }catch (Exception e){
            cacheCommandService.putCache(cacheCommandService.getKey(systemCode, loginName,"check"),
                    String.valueOf(IsEnableEnum.YES.getCode()));
            throw e;
        }
        if (authenticate == null) {
            cacheCommandService.putCache(cacheCommandService.getKey(systemCode, loginName,"check"),
                    String.valueOf(IsEnableEnum.YES.getCode()));
            return Result.error(ResponseCoderEnum.Failed.getCode(), "登录校验失败");
        } else {
            cacheCommandService.putCache(cacheCommandService.getKey(systemCode, loginName,"check"),
                    String.valueOf(IsEnableEnum.NO.getCode()));
            return Result.ok("登录成功，返回token",jwtAnalysisDesign.getToken(authenticate));
        }
    }
    @PostMapping("/regist")
    @ApiOperation(value = "用户注册")
    public Result<String> regist(@RequestBody ILoginRequest request,
                                 @RequestHeader(HttpHeader.SYSTEM_SOURCE_CODE) @ApiIgnore String systemCode) {
        ILoginDTO dto = SecurityBeanUtil.request2dto(request, ILoginDTO.class);
        return Result.ok("用户注册完成，返回身份信息",iLoginCommandService.rigister(dto,systemCode));
    }
    @GetMapping("/role/{bindingStatus}/{roleId}")
    @ApiOperation(value = "角色已绑定/允许绑定用户查询")
    public Result<Page<ILoginVO>> roleBindingLogins(@PathVariable("bindingStatus") String bindingStatus,
                                                    @PathVariable("roleId") Long roleId,
                                                    ILoginPageRequest request) {
        ILoginDTO loginDTO = ILoginAssembler.INSTANCT.convertDto(request);
        CredentialDetail credentialDetail = SecurityBeanUtil.getCredentialDetail();
        Page<ILoginDTO> page = Optional.ofNullable(bindingStatus).filter(code -> IsEnableEnum.YES.getCode().toString().equals(code)).map(
                code -> iLoginCommandService.roleHasBindingLogins(loginDTO, request.getPageNo(), request.getPageSize(), roleId, credentialDetail)
        ).orElseGet(() -> iLoginCommandService.roleUnBindingLogins(loginDTO, request.getPageNo(), request.getPageSize(), roleId, credentialDetail));
        Page<ILoginVO> result = new Page(page.getCurrent(),page.getSize(),page.getTotal());
        result.setRecords( page.getRecords().stream().map(ILoginAssembler.INSTANCT::convertVo).collect(Collectors.toList()));
        return Result.ok("查询成功",result);
    }

    @PostMapping("/role/{bindingStatus}/{loginId}/{roleId}")
    @ApiOperation(value = "账号绑定（解绑）角色")
    public Result<Boolean> roleBindingStatus(@PathVariable("bindingStatus") String bindingStatus,
                                              @PathVariable("roleId") Long roleId,
                                              @PathVariable("loginId") Long loginId){
        return Result.ok("操作成功",relevanceDesign.roleLoginBindingStatus(roleId, loginId, IsEnableEnum.YES.getCode().toString().equals(bindingStatus),
                SecurityBeanUtil.getCredentialDetail()));
    }

    @GetMapping("/info/{id}")
    @ApiOperation(value = "根据账号ID查询登录账号基本信息")
    public Result<ILoginVO> searchById(@ApiParam(required = true, name = "id", value = "待查询账号ID") @PathVariable Long id,
                                       @RequestParam(name = "账号拓展信息",defaultValue = "user")  String moduleName) {
        ILoginDTO iLogin = Optional.ofNullable(iLoginCommandService.searchById(id,moduleName, SecurityBeanUtil.getCredentialDetail()))
                .orElseThrow(() -> new NoDataException("查询账号异常", null));
        return Result.ok(ResponseCoderEnum.Success.getDesc(), ILoginAssembler.INSTANCT.convertVo(iLogin));
    }

    @GetMapping("/allow/{loginName}")
    @ApiOperation(value = "查询账号是否允许使用(false:系统已存在账号;true:不存在或者为当前账号)")
    public Result<Boolean> allowUseLoginName(@ApiParam(required = true, name = "loginName", value = "待查询账号名称") @PathVariable String loginName,
                                            @ApiParam(name = "matchLoginId", value = "待匹配账号Id") Long matchLoginId) {
        return Result.ok(ResponseCoderEnum.Success.getDesc(), iLoginCommandService.allowUseLoginName(loginName, matchLoginId));
    }


    @PutMapping("/info/{id}")
    @ApiOperation(value = "根据主键修改账户信息（此接口不支持修改账号密码）")
    public Result<Boolean> updateById(@ApiParam(required = true, name = "id", value = "待查询账号ID") @PathVariable Long id,
                                      @RequestBody ILoginRequest request) {
        ILoginDTO loginDTO = ILoginAssembler.INSTANCT.convertDto(request);
        loginDTO.setId(id);
        return Result.ok(ResponseCoderEnum.Success.getDesc(), iLoginCommandService.updateById(loginDTO,SecurityBeanUtil.getCredentialDetail()));
    }

    @PostMapping("/info")
    @ApiOperation(value = "创建角色基本信息")
    public Result<ILoginVO> insert(@RequestBody ILoginRequest request) {
        ILoginDTO insert = iLoginCommandService.insert(ILoginAssembler.INSTANCT.convertDto(request),
                SecurityBeanUtil.getCredentialDetail());
        return Result.ok("角色信息创建成功", ILoginAssembler.INSTANCT.convertVo(insert));
    }

    @DeleteMapping("/info")
    @ApiOperation(value = "注销当前账号")
    public Result<Boolean> delete() {
        CredentialDetail credentialDetail = SecurityBeanUtil.getCredentialDetail();
        boolean deleteStatus = false;
        if(Objects.nonNull(credentialDetail)){
            deleteStatus = iLoginCommandService.deleteById(credentialDetail.getLoginId());
        }
        return Result.ok(ResponseCoderEnum.Success.getDesc(), deleteStatus);
    }

    @DeleteMapping("/info/{id}")
    @ApiOperation(value = "注销子账号")
    public Result<Boolean> deleteById(@ApiParam(required = true, name = "id", value = "待注销账号ID") @PathVariable Long id) {
        CredentialDetail credentialDetail = SecurityBeanUtil.getCredentialDetail();
        boolean deleteStatus = false ;
        if(Objects.nonNull(credentialDetail)){
            deleteStatus = iLoginCommandService.deleteByIdHasPreCheck(id, credentialDetail);
        }
        return Result.ok(ResponseCoderEnum.Success.getDesc(), deleteStatus);
    }

    @GetMapping("/page")
    @ApiOperation(value = "分页条件查询账号信息")
    public Result<Page<ILoginVO>> selectPageByConditional(ILoginPageRequest request){
        ILoginDTO loginDTO = ILoginAssembler.INSTANCT.convertDto(request);
        CredentialDetail credentialDetail = SecurityBeanUtil.getCredentialDetail();
        Page<ILoginVO> iLoginVOPage = new Page<>(request.getPageNo(), request.getPageSize());
        if(Objects.nonNull(credentialDetail)){
            Page<ILoginDTO> iLoginPage = iLoginCommandService.selectPageByConditional(loginDTO, request.getPageNo(), request.getPageSize(), credentialDetail);
            iLoginVOPage = new Page<>(iLoginPage.getCurrent(), iLoginPage.getSize(), iLoginPage.getTotal());
            iLoginVOPage.setRecords(iLoginPage.getRecords().stream().map(ILoginAssembler.INSTANCT::convertVo).collect(Collectors.toList()));
        }
        return Result.ok("分页查询用户账号信息完成",iLoginVOPage);
    }

    @GetMapping("/captcha/need")
    @ApiOperation(value = "判断当前是否需要登录验证码")
    public Result<Boolean> hasCaptcha(@ApiParam(name = "principal", value = "身份信息名称") String principal ,
                                      @RequestHeader(HttpHeader.SYSTEM_SOURCE_CODE) @ApiIgnore String systemCode){
        Assert.hasText(principal,"身份信息名称不能为空,请检查");
        String value = cacheCommandService.getValue(cacheCommandService.getKey(systemCode, principal,"check"));
        return Result.ok("验证码获取成功",String.valueOf(IsEnableEnum.YES.getCode()).equals(value));
    }

    @GetMapping("/captcha")
    @ApiOperation(value = "登录验证码")
    public Result<String> createCaptcha(@ApiParam(name = "width", value = "生成图片像素宽度",defaultValue = "200") @RequestParam(value = "width",defaultValue = "200") Integer width ,
                                        @ApiParam(name = "height", value = "生成图片像素高度",defaultValue = "100") @RequestParam(value = "height",defaultValue = "100") Integer height ,
                                        @ApiParam(name = "principal", value = "身份信息名称") String principal ,
                                        @RequestHeader(HttpHeader.SYSTEM_SOURCE_CODE) @ApiIgnore String systemCode){
        Assert.hasText(principal,"身份信息名称不能为空,请检查");
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(width, height);
        lineCaptcha.setGenerator(randomGenerator);
        String code = lineCaptcha.getCode();
        cacheCommandService.putCache(cacheCommandService.getKey(systemCode,principal,"captcha"),code);
        return Result.ok("验证码获取成功",lineCaptcha.getImageBase64());
    }

    @PutMapping("/password")
    @ApiOperation(value = "修改当前账号密码")
    public Result<Boolean> updateCurrentPassword(@RequestBody ILoginUpdatePasswordRequest request) {
        String oldPassword = request.getOldPassword();
        if(StrUtil.isBlank(oldPassword)){
            return Result.error(ResponseCoderEnum.NotValidException.getCode(), "请求旧密码参数不存在,请检查");
        }
        String newPassword = request.getNewPassword();
        if(StrUtil.isBlank(newPassword)){
            return Result.error(ResponseCoderEnum.NotValidException.getCode(), "请求新密码参数不存在,请检查");
        }
        if(newPassword.equals(oldPassword)){
           return Result.error(ResponseCoderEnum.NotValidException.getCode(), "新旧密码不能相同,请检查");
        }
        boolean flag = iLoginCommandService.updateCurrentPassword(oldPassword,newPassword,SecurityBeanUtil.getCredentialDetail());
        return Result.ok(ResponseCoderEnum.Success.getDesc(), flag);
    }

    @PutMapping("/password/{loginId}")
    @ApiOperation(value = "修改子账号密码")
    public Result<Boolean> updatePassword(@PathVariable("loginId") Long loginId,
                                          @RequestBody ILoginPasswordRequest request) {
        String password = request.getPassword();
        if(StrUtil.isBlank(password)){
            return Result.error(ResponseCoderEnum.NotValidException.getCode(), "待修改密码参数不存在,请检查");
        }
        boolean flag = iLoginCommandService.updateSubPassword(loginId,password,SecurityBeanUtil.getCredentialDetail());
        return Result.ok(ResponseCoderEnum.Success.getDesc(), flag);
    }
}
